import { effect } from "../reactivity/effect";
import { EMPTY_OBJECT, isObject, isOn } from "../shared/index";
import { ShapeFlages } from "../shared/ShapeFlags";
import { createComponentInstance, setupComponent } from "./component";
import { CreateAppAPI } from "./createApp";
import { Fragment, Text, getShapeFlag } from "./vnode";


export function createRenderer(options) {

    const {
        createElement,
        patchProp: hostPatchProp,
        insert,
        remove: hostRemove,
        setElementText: hostSetElementText
    } = options;


    function render(vnode, container, parentComponent) {
        // 调用patch 方法
        patch(null, vnode, container, null)
    }


    /**
     * 
     * @param n1 老的节点 vnode 
     * @param n2 新的节点 vnode 
     * @param container 
     * @param parentComponent 
     */
    function patch(n1, n2, container, parentComponent) {
        // 处理组件
        // 1.检查组件是什么类型
        // 2.处理组件或者Element逻辑


        let { type, shapeFlag, } = n2;
        if (!type) {
            type = Text;
        }

        switch (type) {
            case Fragment:
                // 处理 Fragment 节点
                processFragment(n1, n2, container, parentComponent);
                break;
            case Text:
                // 处理 Text 节点
                processText(n1, n2, container);
                break;
            default:
                if (shapeFlag & ShapeFlages.STATEFUL_COMPONENT) {
                    // 处理组件 (Stateful_Component)
                    processComponent(n1, n2, container, parentComponent);
                } else if (shapeFlag & ShapeFlages.ELEMENT) {
                    // 处理 Element 节点
                    processElement(n1, n2, container, parentComponent);
                }
                break;
        }
    }

    /**
     * 处理纯文本节点
     * @param n2 
     * @param container 
     */
    function processText(n1, n2: any, container: any) {
        const text = n2.children;
        // 创建一个文本节点，并且给当前的 vnode 的 el 属性赋值
        const textNode = (n2.el = document.createTextNode(text));
        // 在父容器上拼上 textNode 节点
        container.append(textNode)
    }

    /**
     * 处理分段块节点
     * @param n2 
     * @param container 
     */
    function processFragment(n1, n2: any, container: any, parentComponent) {
        // 挂载 Fragment 的子节点组件
        mountChildren(n2.children, container, parentComponent);
    }

    function processComponent(n1, n2: any, container: any, parentComponent) {
        // 挂载 Component 组件
        mountComponent(n1, n2, container, parentComponent);
    }


    function mountComponent(n1, n2: any, container: any, parentComponent) {
        // 创建一个组件实例对象
        const instance = createComponentInstance(n2, parentComponent);

        // 初始化组件
        setupComponent(instance);

        // 调用 render 函数，经过初始化组件，instance.render 已经有值
        setupRenderEffect(instance, container);
    }


    function setupRenderEffect(instance: any, container) {
        effect(() => {
            // isMoutend 为 false 代表是初次渲染，新增(init 初始化）逻辑
            if (!instance.isMounted) {
                console.log("render effect 的 init 逻辑");
                // 获取经过 setupComponent 完成的 proxy 代理对象
                const { proxy } = instance;

                // 调用组件实例的 render ，返回一个虚拟节点树
                // subTree 也是一个 vnode 节点。 
                // * 改变 根元素 render 中的 this 指向为代理对象（这里很重要）
                // 给实例上的 subTree赋值，记录上次渲染的子节点树
                const subTree = (instance.subTree = instance.render.call(proxy));

                // subTree(vnode) -> 继续向下递归派发 -> patch
                patch(null, subTree, container, instance);

                // *到达这一步骤，才代表所有的节点都处理完成（这里也很重要）
                // 此时 subTree 就是 render 函数中的 根元素
                console.log('subTree', subTree);
                console.log('instance', instance);

                // 当前实例已挂载
                instance.isMounted = true;

                instance.vnode.$el = subTree.el;
            } else {
                console.log("render effect 的 update 逻辑");

                const { proxy } = instance;
                const subTree = instance.render.call(proxy);
                const prevSubTree = instance.subTree;

                // 当时更新逻辑时，获取到上一次的节点树和当前节点树，继续向下派发
                patch(prevSubTree, subTree, container, instance);
            }
        })
    }


    function processElement(n1, n2: any, container: any, parentComponent) {
        if (!n1) {
            // 初始化 element
            mountElement(n2, container, parentComponent);
        } else {
            // 更新 element
            patchElement(n1, n2, container,parentComponent);
        }
    }

    function mountElement(vnode: any, container: HTMLElement, parentComponent) {
        console.log("mountElement, 初次挂载 Element 节点");
        // 创建 el 
        // const el: HTMLElement = document.createElement(vnode.type);
        const el: HTMLElement = createElement(vnode.type);
        // 给当前的 vnode 的 el 属性赋值，当前 vnode 可能代表 根节点，也可能代表根节点中的任何子节点
        // 顺序为： root -> children[1] -> children[1.1] ->children[1.1.1] -> xxx
        vnode.el = el;
        console.log('vnode', vnode);

        const { props, children, shapeFlag } = vnode;
        // 处理 el 的文本内容 或者 arrary 子节点
        if (shapeFlag & ShapeFlages.TEXT_CHILDREN) {
            el.textContent = children;
        } else if (shapeFlag & ShapeFlages.ARRAY_CHILDREN) {
            // vnode 数组的情况
            // 挂载子节点
            mountChildren(children, el, parentComponent);
        }

        // 处理 attribute 
        for (const key in props) {
            let value = props[key];

            hostPatchProp(el, key, null, value);
        }

        // container.append(el);
        insert(el, container);
    }


    function patchElement(n1: any, n2: any, container: HTMLElement,parentComponent) {
        console.log("patchElement, 更新 Element 节点");
        console.log("n1", n1);
        console.log("n2", n2);
        console.log("container", container);

        const oldProps = n1.props || EMPTY_OBJECT;
        const newProps = n2.props || EMPTY_OBJECT;

        // 这次的 n2 会变成 下次的 n1, 但此时 n2 没有 el ，所以把n1.el 赋值给 n2.el 
        const el = (n2.el = n1.el);

        // 处理子节点的更新
        patchChildren(n1, n2, el, parentComponent);

        // 处理 props 的更新
        patchProps(oldProps, newProps, el)
    }


    function patchChildren(n1, n2, container, parentComponent) {

        let prevShapeFlag = n1.shapeFlag; // 原类型 
        let { shapeFlag } = n2; // 目标类型

        // 处理目标类型为 text 的情况
        if (shapeFlag & ShapeFlages.TEXT_CHILDREN) {
            if (prevShapeFlag & ShapeFlages.ARRAY_CHILDREN) {
                // Array to text 逻辑
                // 步骤1： 把老的 array children 清空 
                unMountChildren(n1.children);
            }

            // 如果因为 n2 目标类型已经是 文本， 所以原来的 n1.children(不管是 text 还是 array ) 和 n2.children 比较
            // 如果不相等，则直接替换 文本内容
            if (n1.children !== n2.children) {
                // array to text 步骤2: 设置text 
                // text to text 步骤1: 设置text
                hostSetElementText(container, n2.children); // container(父节点), n2.children (新的文本)
            }
        } else // 目标类型为 array 的情况 
        {
            if (prevShapeFlag & ShapeFlages.TEXT_CHILDREN) {
                // text to array 情况

                // text to array 步骤1: 清空文本内容
                hostSetElementText(container, "");

                debugger
                // text to array 步骤2: 挂载子节点
                mountChildren(n2.children, container, parentComponent);
            }
        }

    }

    function patchProps(oldProps, newProps, el) {
        if (oldProps === newProps)
            return

        /**
         * 该方法需要结合 updateProps demo 一起调试理解。
         * 此处 props 代表 Elemet 的属性 Attribute
         */
        for (const key in newProps) {
            const prevProp = oldProps[key];
            const nextProp = newProps[key];

            // 情况1：props 更新了，重新设置 element 的属性值
            if (prevProp !== nextProp) {
                hostPatchProp(el, key, prevProp, nextProp)
            }

        }

        // 情况2： props 的值设置为 null 或者 undefined 
        // 情况2在 hostPatchProp 中处理

        if (oldProps === EMPTY_OBJECT)
            return;

        // 情况3 ： 老的 props 值存在的 但新的 props 不存在（被删除）
        for (const key in oldProps) {
            if (!(key in newProps)) {
                // 不存在于新的 props 中，此时需要删除 Attribute
                // 第四个参数 nextVal = null 代表删除 Attribute
                hostPatchProp(el, key, oldProps[key], null)
            }
        }
    }


    function mountChildren(childrens, contariner, parentComponent) {
        for (const index in childrens) {
            // 多个子 vnode， 重新向下层派发递归
            patch(null, childrens[index], contariner, parentComponent);
        };
    }

    function unMountChildren(childrens) {
        for (let i = 0; i < childrens.length; i++) {
            const el = childrens[i].el;

            // 调用 hostRemove （runtime-demo 中的 remove)  删除el
            hostRemove(el);
        }
    }





    return {
        createApp: CreateAppAPI(render)
    }
}
